package com.szwistar.common.meta;

import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.dao.DaoManager;
import com.j256.ormlite.dao.ReferenceObjectCache;
import com.j256.ormlite.stmt.PreparedQuery;
import com.j256.ormlite.stmt.QueryBuilder;
import com.j256.ormlite.support.ConnectionSource;
import com.szwistar.common.ERR;
import com.szwistar.common.comm.jsonrpc.JsonRpcMessageCall;
import com.szwistar.common.comm.jsonrpc.JsonRpcMessageReturn;
import com.szwistar.common.comm.jsonrpc.JsonRpcService;
import com.szwistar.common.datastruct.JsonMapper;
import com.szwistar.common.datastruct.Reflector;
import com.szwistar.common.datastruct.Utils;
import com.szwistar.common.meta.condition.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationTargetException;
import java.sql.SQLException;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 一个比较通用的数据服务对象
 * <p>这个数据服务试图为 POJO 数据模型提供 DAO/PO/DTO/VO/DataBind/Cache/RPC 等服务。
 * <li>DO - Domain Model, 领域模型。指对一个业务领域中的主要业务对象进行建模形成的概念模型。它是其它模型的基础。</li>
 * <li>POJO - Plain Ordinary Java Object, 简单 Java 对象，指不从任何类派生，不实现任何接口，没有被其它框架侵入，有无参构造函数，每个字段都有 getter/setter 的 Java 类。
 * <li>PO - Persistent Object, 持久化对象，为了将对象从持久化存储保存/取出而提供的实体对象，可通过 Serialize/Deserialize 操作从数据块中保存/取出 PO 对象。</li>
 * <li>ORM - Object-Relation Mapping, 对象-关系映射，指的是将内存中的对象映射到关系型数据库表的技术手段，用于将对象从数据库保存/取出。</li>
 * <li>DAO - Data Access Object, 为了将对象从数据库保存/取出而提供的服务对象，帮助应用完成对象的增改删查等操作。</li>
 * <li>DTO - Data Transfer Object 数据传输对象，是为分布式应用或跨语言应用提供的数据传输模型，要考虑数据兼容性和传输效率。分为 IDL 型接口和自解释型接口。</li>
 * <li>VO - View/Value Object, 视图对象/值对象。用于给前端显示的对象，一般只传递有需要的参数以保障数据安全。</li>
 * <li>VDB - View Data Binding, 视图数据绑定</li>
 * </p>
 * <p>它可以承担以下任务：
 * <li>做为标准的 JsonRpcService，提供对数据的增加、修改、删除、查询服务接口
 * <li>提供内存对象缓存服务
 * <li>提供数据库存储服务
 * <li>提供数据前端编辑表单自动生成所需的 meta 信息
 * @param <T> 要服务的数据类型，要求必须有一个名称为 "id" 的字段 或 @DefField 标记为 id 的字段 或 @DatabaseField 标记为 id 的字段
 */
public class ObjectService<T> implements JsonRpcService {
    protected static final Logger LOG = LoggerFactory.getLogger(ObjectService.class);

    // 包含所有数据服务的映射表
    protected static Map<String, ObjectService<?>> serviceMap = new HashMap<>();
    public static Map<String, ObjectService<?>> getServiceMap() { return serviceMap; }

    /**
     * 获取已经注册的数据服务
     * @param name 数据服务名
     */
    public static ObjectService<?> getService(String name) { return serviceMap.get(name); }

    /**
     * 创建指定类型的数据服务
     * @param dbconn 数据连接
     * @param name 数据服务名
     * @param clazz 服务的数据类名
     * @param loadAll 是否把数据中的所有对象加载到内存缓存
     * @see <a href="https://ormlite.com/javadoc/ormlite-core/doc-files/ormlite.html#Object-Caches">ORMLite 对象缓存</a>
     */
    public static <T> ObjectService<T> createService(ConnectionSource dbconn, String name, Class<T> clazz, boolean loadAll) throws SQLException {
        @SuppressWarnings("unchecked")
        ObjectService<T> svc = (ObjectService<T>) serviceMap.get(name);
        if(svc != null) return svc;

        svc = new ObjectService<>();
        svc.dao = DaoManager.createDao(dbconn, clazz);
        svc.name = name;
        svc.clazz = clazz;
        if(loadAll) {
            // 使用 ORMLite-DAO 自己的 Cache 机制:
            // Cache 算法1：ReferenceObjectCache.makeWeakCache()
            //svc.dao.setObjectCache(true);
            // Cache 算法2：引用
            svc.dao.setObjectCache(ReferenceObjectCache.makeSoftCache());
            // Cache 算法3：引用
            //svc.dao.setObjectCache(new LruObjectCache(100));
            // 查询一次，以便构建起缓存
            List<T> list = svc.dao.queryForAll();
        }

        serviceMap.put(name, svc);
        return svc;
    }

    // 服务名称
    protected String name;
    @Override
    public String getName() { return name; }

    // 服务的数据类名
    protected Class<T> clazz;
    // 数据访问对象
    protected Dao<T, Integer> dao;
    public Dao<T, Integer> getDao() { return dao; }

    /**
     * 获取数据前端编辑表单自动生成所需的 meta 信息
     * @return meta 信息
     */
    public ObjectRegistry.ObjectMetaInfo getMetaInfo() {
        return ObjectRegistry.register(clazz);
    }

    /**
     * 获取数据前端编辑表单自动生成所需的 meta 信息
     * @param request 不需要 JSON 参数
     * @return meta 信息
     */
    public JsonRpcMessageReturn getMetaInfo(JsonRpcMessageCall request) {
        return JsonRpcMessageReturn.ok(request, getMetaInfo());
    }

    /**
     * 创建新对象
     * @param obj 要创建的对象（自增ID可为空，会自动分配）
     * @return 保存到数据库之后的对象（自增ID已自动分配）
     */
    public T _create(T obj) throws SQLException {
        dao.create(obj);
        return obj;
    }

    /**
     * 根据提交的参数，创建对象
     * @param request 其中 JSON 参数为一个 JSON 对象，其字段应与目标类字段相同，其中必填字段不能为空
     * @return 成功时返回新创建对象的唯一ID；失败时返回标准错误消息。
     */
    public JsonRpcMessageReturn create(JsonRpcMessageCall request) {
        T obj;
        Integer id;

        // 检查必填字段是否为空
        Map<String, Object> params = request.getParamMap();
        for(ObjectRegistry.FieldInfo info: ObjectRegistry.register(clazz).getFields()) {
            if("true".equalsIgnoreCase(info.getEnable()) && "true".equalsIgnoreCase(info.getRequired()) && params.get(info.getName()) == null) {
                return JsonRpcMessageReturn.error(request, ERR.TOO_FEW_PARAMS, "字段 '" + info.getName() + "' 不能为空！");
            }
        }

        // 从参数中生成对象
        try {
            obj = request.parseParam(clazz);
        } catch(Exception e) {
            return JsonRpcMessageReturn.error(request, ERR.BAD_PARAM, "解析参数失败！" + e.getMessage());
        }

        // 保存到数据库
        try {
            dao.create(obj);
            id = dao.extractId(obj);
        } catch(Exception e) {
            return JsonRpcMessageReturn.error(request, ERR.DB_INERT, "在数据库中创建对象失败！" + e.getMessage());
        }

        // 返回新创建对象的唯一 ID
        return JsonRpcMessageReturn.ok(request, id);
    }

    /**
     * 保存更新到数据库
     * @param obj 要保存的对象
     * @return 保存的对象
     */
    public T save(T obj) throws SQLException {
        dao.update(obj);
        return obj;
    }

    /**
     * 更新一个已有的对象
     * @param request 其中 JSON 参数为一个 JSON 对象，其字段应与目标类字段相同，其中 ID 字段不能为空
     */
    public JsonRpcMessageReturn update(JsonRpcMessageCall request) {
        T obj, objNew;
        Integer id;

        // 从参数中生成对象
        try {
            objNew = request.parseParam(clazz);
            id = dao.extractId(objNew);
            if(id == null) return JsonRpcMessageReturn.error(request, ERR.BAD_PARAM, "ID 字段不能为空！");
        } catch(Exception e) {
            return JsonRpcMessageReturn.error(request, ERR.BAD_PARAM, "解析参数失败！" + e.getMessage());
        }

        try {
            // 查询对象
            obj = dao.queryForId(id);
            if(obj == null) return JsonRpcMessageReturn.error(request, ERR.BAD_PARAM, "指定的对象不存在！");

            // 逐个字段更新
            for(String name: request.getParamMap().keySet()) {
                // 取得更新后的字段
                Object val = Reflector.getFieldValue(objNew, name);
                // 跳过空字段
                if(val == null) continue;
                // 更新字段
                Reflector.setPorpertyOrField(obj, name, val);
            }

            // 保存到数据库
            dao.update(obj);
        } catch(SQLException e) {
            return JsonRpcMessageReturn.error(request, ERR.DB_UPDATE, "更新数据库中对象失败！" + e.getMessage());
        } catch(InvocationTargetException | IllegalAccessException e) {
            return JsonRpcMessageReturn.error(request, ERR.DB_UPDATE, "更新对象属性失败！" + e.getMessage());
        }

        return JsonRpcMessageReturn.ok(request, null);
    }

    /**
     * 删除指定 ID 的对象
     * @param ids 数组中是要删除的对象ID
     */
    public void delete(Collection<Integer> ids) throws SQLException {
        // 从数据库中删除
        dao.deleteIds(ids);
    }

    /**
     * 删除指定 ID 的对象
     * @param request 其中 JSON 参数为一个数组，数组中是要删除的对象ID
     */
    public JsonRpcMessageReturn delete(JsonRpcMessageCall request) {
        try {
            // 取出要删除的对象 ID 数组
            List<Integer> ids = JsonMapper.fromJsonAsListOf(request.getParams(), Integer.class);
            delete(ids);
        } catch(SQLException e) {
            return JsonRpcMessageReturn.error(request, ERR.DB_DELETE, "从数据库中删除对象失败！" + e.getMessage());
        } catch(Exception e) {
            return JsonRpcMessageReturn.error(request, ERR.BAD_PARAM, "解析参数失败！" + e.getMessage());
        }
        return JsonRpcMessageReturn.ok(request, null);
    }

    /**
     * 查询所有对象
     * @return 所有对象数组
     */
    public Collection<T> getAll() throws SQLException {
        return dao.queryForAll();
    }

    /**
     * 查询所有对象
     * @param request 不需要 JSON 参数
     * @return 所有对象数组
     */
    public JsonRpcMessageReturn getAll(JsonRpcMessageCall request) {
        try {
            return JsonRpcMessageReturn.ok(request, getAll());
        } catch(SQLException e) {
            return JsonRpcMessageReturn.error(request, ERR.DB_QUERY, "从数据库中查询对象失败！" + e.getMessage());
        }
    }

    /**
     * 获取指定ID的对象
     * @param id 指定对象的ID
     * @return 对象
     */
    public T getById(Integer id) {
        try {
            return dao.queryForId(id);
        } catch(SQLException e) {
            return null;
        }
    }

    /**
     * 获取指定ID的对象
     * @param request 其中 JSON 参数为指定对象的ID
     * @return 对象
     */
    public JsonRpcMessageReturn getById(JsonRpcMessageCall request) {
        try {
            Integer id = request.parseParam(Integer.class);
            if(id == null) return JsonRpcMessageReturn.error(request, ERR.BAD_PARAM, "未提供有效ID！");
            T obj = dao.queryForId(id);
            if(obj == null) return JsonRpcMessageReturn.error(request, ERR.NOT_FOUND, "对象不存在！ID=" + id);
            return JsonRpcMessageReturn.ok(request, obj);
        } catch(SQLException e) {
            return JsonRpcMessageReturn.error(request, ERR.DB_QUERY, "从数据库中查询对象失败！" + e.getMessage());
        }
    }

    /**
     * 获取指定ID的对象
     * @param ids 指定对象的ID数组
     * @return 对象数组
     */
    public Collection<T> getByIds(Integer[] ids) {
        try {
            return get(new ConditionIn<Integer>("id", ids));
        } catch(Exception e) {
            return null;
        }
    }

    /**
     * 获取指定ID列表的对象
     * @param request 其中 JSON 参数为一个数组，数组中是要获取的对象ID
     * @return 对象数组
     */
    public JsonRpcMessageReturn getByIds(JsonRpcMessageCall request) {
        try {
            // 取出对象 ID 数组
            Integer[] ids = JsonMapper.fromJsonAsArrayOf(request.getParams(), Integer.class);
            return JsonRpcMessageReturn.ok(request, getByIds(ids));
        } catch(SQLException e) {
            return JsonRpcMessageReturn.error(request, ERR.DB_DELETE, "从数据库中查询对象失败！" + e.getMessage());
        } catch(Exception e) {
            return JsonRpcMessageReturn.error(request, ERR.BAD_PARAM, "解析参数失败！" + e.getMessage());
        }
    }

    protected PreparedQuery<T> buildQuery(Condition[] conditions) throws Exception {
        QueryBuilder<T, Integer> qb = dao.queryBuilder();
        com.j256.ormlite.stmt.Where<T, Integer> where = qb.where();
        boolean isFirst = true;

        for(Condition cond: conditions) {
            if(!isFirst) where.and();
            if(cond instanceof ConditionIsNull) where.isNull(cond.field);
            else if(cond instanceof ConditionIsNotNull) where.isNotNull(cond.field);
            //else if(cond instanceof ConditionIsTrue) where.;
            //else if(cond instanceof ConditionIsFalse) where.isNotNull(cond.field);
            else if(cond instanceof ConditionEqual) where.eq(cond.field, ((ConditionEqual<?>)cond).value);
            else if(cond instanceof ConditionNotEqual) where.ne(cond.field, ((ConditionNotEqual<?>)cond).value);
            else if(cond instanceof ConditionLike) where.like(cond.field, ((ConditionLike)cond).value);
            else if(cond instanceof ConditionGreatThan) where.gt(cond.field, ((ConditionGreatThan<?>)cond).value);
            else if(cond instanceof ConditionGreatEqual) where.ge(cond.field, ((ConditionGreatEqual<?>)cond).value);
            else if(cond instanceof ConditionLittleThan) where.lt(cond.field, ((ConditionLittleThan<?>)cond).value);
            else if(cond instanceof ConditionLittleEqual) where.le(cond.field, ((ConditionLittleEqual<?>)cond).value);
            else if(cond instanceof ConditionBetween) where.between(cond.field, ((ConditionBetween<?>)cond).low, ((ConditionBetween<?>)cond).high);
            else if(cond instanceof ConditionIn) where.in(cond.field, (Object[])((ConditionIn<?>)cond).values);
            else if(cond instanceof ConditionNotIn) where.notIn(cond.field, (Object[])((ConditionNotIn<?>)cond).values);
            isFirst = false;
        }

        return qb.prepare();
    }

    /**
     * 多个任意条件查询
     * @param conditions 查询结构数组
     * @return 符合条件的对象数组
     */
    public List<T> get(Condition ...conditions) throws Exception {
        PreparedQuery<T> query = buildQuery(conditions);
        return dao.query(query);
    }

    /**
     * 任意条件查询
     * @param request 其中 JSON 参数为一个查询结构数组
     * @return 符合条件的对象数组
     */
    public JsonRpcMessageReturn get(JsonRpcMessageCall request) {
        try {
            // 从 JSON 中解析条件列表
            Condition[] conditions = JsonMapper.fromJsonAsArrayOf(request.getParams(), Condition.class);
            List<T> objs = get(conditions);
            return JsonRpcMessageReturn.ok(request, objs);
        } catch(SQLException e) {
            return JsonRpcMessageReturn.error(request, ERR.DB_DELETE, "从数据库中查询对象失败！" + e.getMessage());
        } catch(Exception e) {
            return JsonRpcMessageReturn.error(request, ERR.BAD_PARAM, "解析参数失败！" + e.getMessage());
        }
    }

    /**
     * 按 name 字段查询
     * @param name 对象名称
     * @return 符合条件的对象数组
     */
    public List<T> getByName(String name) throws Exception {
        return get(new ConditionEqual<>("name", name));
    }

    // 用于 select 查询的参数结构
    public static class SelectParams {
        public Condition[] conditions;
        public String[] fields;
    }

    /**
     * 查询所有对象，并只返回只指定字段
     * @param request 其中参数为 JSON 对象:<pre>{
     *         "conditions": [{"type":"eq","field":"id","value":1},...], // 查询条件数组
     *         "fields":["id","name"]       // 字段名称字符串数组
     *     }</pre>
     * @return 二维数组，第一维表示行列表，第二维是字段列表
     */
    public JsonRpcMessageReturn select(JsonRpcMessageCall request) {
        try {
            // 从 JSON 中解析查询参数
            SelectParams params = JsonMapper.fromJson(request.getParams(), SelectParams.class);
            // 如果没有参数，则返回所有记录的所有字段
            if(params == null) return JsonRpcMessageReturn.error(request, ERR.BAD_PARAM, "未提供有效的参数！");
            if(Utils.isEmpty(params.fields)) return JsonRpcMessageReturn.error(request, ERR.BAD_PARAM, "未提供要查询的字段名称列表！");

            // 查出所有符合条件的对象
            Collection<T> all = null;
            if(Utils.isEmpty(params.conditions)) {
                // 如果没有查询条件，则返回所有对象
                all = getAll();
            } else {
                all = get(params.conditions);
            }

            Object[][] res = new Object[all.size()][params.fields.length];
            int row = 0, col = 0;
            for(T obj : all) {
                col = 0;
                for(String fieldName : params.fields) {
                    res[row][col] = Reflector.getPorpertyOrField(obj, fieldName);
                    col++;
                }
                row++;
            }
            return JsonRpcMessageReturn.ok(request, res);
        } catch(SQLException e) {
            return JsonRpcMessageReturn.error(request, ERR.DB_DELETE, "从数据库中查询对象失败！" + e.getMessage());
        } catch(Exception e) {
            return JsonRpcMessageReturn.error(request, ERR.BAD_PARAM, "解析参数失败！" + e.getMessage());
        }
    }

    /**
     * 查询所有对象，并只返回 "id","name" 字段
     * @param request 无需参数
     * @return '[{"key":0,"name":"中国"},{"key":1,"name":"美国"}]'
     */
    public JsonRpcMessageReturn getDict(JsonRpcMessageCall request) {
        try {
            Collection<T> all = getAll();
            Object[] res = new Object[all.size()];
            int row = 0;
            for(T obj: all) {
                Map<String, Object> robj = new HashMap<>();
                robj.put("key", Reflector.getPorpertyOrField(obj, "id"));
                robj.put("name", Reflector.getPorpertyOrField(obj, "name"));
                res[row] = robj;
                row++;
            }
            return JsonRpcMessageReturn.ok(request, res);
        } catch(SQLException e) {
            return JsonRpcMessageReturn.error(request, ERR.DB_DELETE, "从数据库中查询对象失败！" + e.getMessage());
        } catch(Exception e) {
            return JsonRpcMessageReturn.error(request, ERR.BAD_PARAM, "解析参数失败！" + e.getMessage());
        }
    }

    /**
     * 查询所有对象，并只返回 "name" 字段列表
     * @param request 无需参数
     * @return '["中国","美国"...]'
     */
    public JsonRpcMessageReturn getNames(JsonRpcMessageCall request) {
        try {
            Collection<T> all = getAll();
            Object[] res = new Object[all.size()];
            int row = 0;
            for(T obj: all) {
                res[row] = Reflector.getPorpertyOrField(obj, "name");
                row++;
            }
            return JsonRpcMessageReturn.ok(request, res);
        } catch(SQLException e) {
            return JsonRpcMessageReturn.error(request, ERR.DB_DELETE, "从数据库中查询对象失败！" + e.getMessage());
        } catch(Exception e) {
            return JsonRpcMessageReturn.error(request, ERR.BAD_PARAM, "解析参数失败！" + e.getMessage());
        }
    }
}
